/*
** $Id: ltablib.c $
** Library for Table Manipulation
** See Copyright Notice in lua.h
** 这段代码是C语言编写的，是Lua语言中的一个库文件，主要用来提供表格操作的功能。
** 该库提供了许多函数，可以用来创建、操作和管理Lua中的表格。
** 这些函数可以用于在Lua中执行诸如查找、插入、删除、排序等表格操作。
** 该库还提供了将表格转换为字符串和将字符串转换为表格的函数。
*/

#define ltablib_c
#define LUA_LIB

/*
 * ltablib_c - 这是一个定义文件，用于声明表库的C函数。
 * LUA_LIB - 定义这个符号表明这是一个Lua库。
 */

#include "lprefix.h" // 包含预定义头文件，为平台特定的配置。

// 下面包含标准库头文件。
#include <limits.h> // 包含各种平台特定的限制值。
#include <stddef.h> // 包含标准尺寸和指针相关的定义。
#include <string.h> // 包含字符串操作的函数声明。

// 包含Lua的主头文件。
#include "lua.h"

// 包含Lua辅助库的头文件，提供很多辅助函数。
#include "lauxlib.h"
// 包含Lua标准库的头文件。
#include "lualib.h"


/*
** 定义一个对象必须实现的操作，以模仿表的行为
** （有些函数只需要其中的一些操作）
*/

/* 定义访问类型枚举 */
#define TAB_R	1			/* 读操作 */
#define TAB_W	2			/* 写操作 */
#define TAB_L	4			/* 长度操作 */
#define TAB_RW	(TAB_R | TAB_W)		/* 读写操作 */

/*
** 辅助函数：获取表的长度
** 参数：
** L - Lua状态机的指针
** n - 表在Lua栈上的索引
** w - 操作标志，用于指定函数的行为
** 返回值：
** 返回表的元素数量。
*/
#define aux_getn(L,n,w)	(checktab(L, n, (w) | TAB_L), luaL_len(L, n))


/**
 * 检查给定的Lua表中是否存在指定的键。
 * 
 * @param L 指向当前Lua状态的指针，表示正在操作的Lua环境。
 * @param key 指向要检查的键的C字符串指针。
 * @param n 指示lua_rawget应该在Lua栈上相对定位到的位置。负数表示从栈顶向下计数。
 * @return 如果指定的键存在于Lua表中，则返回1；如果不存在，返回0。
 */
static int checkfield (lua_State *L, const char *key, int n) {
  lua_pushstring(L, key); // 将键作为字符串压入Lua栈。
  return (lua_rawget(L, -n) != LUA_TNIL); // 直接从栈顶向下数第n个位置处获取键对应的值，检查是否为非空值。
}


/*
** 检查 'arg' 是否是一个表或者可以像一个表那样行为（即，具有带有所需元方法的元表）
*/
static void checktab (lua_State *L, int arg, int what) {
  if (lua_type(L, arg) != LUA_TTABLE) {  /* 如果它不是一个表？ */
    int n = 1;  /* 需要弹出的元素数量 */
    if (lua_getmetatable(L, arg) &&  /* 必须有元表 */
        (!(what & TAB_R) || checkfield(L, "__index", ++n)) &&
        (!(what & TAB_W) || checkfield(L, "__newindex", ++n)) &&
        (!(what & TAB_L) || checkfield(L, "__len", ++n))) {
      lua_pop(L, n);  /* 弹出元表和已检查的元方法 */
    }
    else
      luaL_checktype(L, arg, LUA_TTABLE);  /* 强制出错 */
  }
}


/*
 * 将新元素插入到Lua表中。
 *
 * 参数：
 *   L - Lua状态机指针，表示当前的Lua环境。
 *
 * 返回值：
 *   返回0，表示没有错误发生。
 */
static int tinsert (lua_State *L) {
  lua_Integer pos;  /* 插入新元素的位置 */
  lua_Integer e = aux_getn(L, 1, TAB_RW); /* 获取表中元素的数量 */
  e = luaL_intop(+, e, 1);  /* 计算第一个空位置的索引 */
  
  switch (lua_gettop(L)) { /* 根据传入的参数数量进行不同处理 */
    case 2: {  /* 当只有两个参数时 */
      pos = e;  /* 将新元素插入到表的末尾 */
      break;
    }
    case 3: {
      lua_Integer i;
      pos = luaL_checkinteger(L, 2);  /* 获取第二个参数作为插入位置 */
      /* 检查插入位置是否在有效范围内 */
      luaL_argcheck(L, (lua_Unsigned)pos - 1u < (lua_Unsigned)e, 2,
                       "position out of bounds位置越界");
      for (i = e; i > pos; i--) {  /* 将后续元素依次后移 */
        lua_geti(L, 1, i - 1);
        lua_seti(L, 1, i);  /* t[i] = t[i - 1] */
      }
      break;
    }
    default: {
      /* 如果参数数量不正确，则报错 */
      return luaL_error(L, "wrong number of arguments to 'insert'向 '插入' 函数传递的参数数量错误");
    }
  }
  lua_seti(L, 1, pos);  /* 在指定位置插入元素 */
  return 0;
}


/*
 * 从Lua表中移除指定位置的元素
 * 参数：
 *   L - Lua状态机指针
 *   1 - 表索引
 *   2 - 要移除的元素的位置索引（可选，默认为表的最后一个元素）
 * 返回值：
 *   1 - 总是返回1，表示移除了一个元素
 */

static int tremove (lua_State *L) {
  // 获取表中元素的数量
  lua_Integer size = aux_getn(L, 1, TAB_RW);
  // 获取要移除的位置索引，如果未提供则默认为表的长度
  lua_Integer pos = luaL_optinteger(L, 2, size);
  
  // 如果提供了位置索引，验证其有效性
  if (pos != size) { 
    // 检查位置索引是否在合法范围内 [1, size + 1]
    luaL_argcheck(L, (lua_Unsigned)pos - 1u <= (lua_Unsigned)size, 2,
                     "position out of bounds位置越界");
  }
  
  // 获取要移除的元素
  lua_geti(L, 1, pos);  
  // 将表中后续元素向前移动，覆盖要移除的元素
  for ( ; pos < size; pos++) {
    lua_geti(L, 1, pos + 1);
    lua_seti(L, 1, pos);  /* t[pos] = t[pos + 1] */
  }
  
  // 将nil设置到被移除元素的位置，实现移除操作
  lua_pushnil(L);
  lua_seti(L, 1, pos);  /* remove entry t[pos] */
  
  return 1; // 表示成功移除一个元素
}


/*
** 将表中的元素（1[f]，..., 1[e]）复制到（tt[t]，tt[t+1]，...）中。尽可能地按递增顺序复制，
** 这对重新哈希更有利。"可能"意味着目标范围在原始范围之后，或小于原始范围，或复制到另一个表中。
**
** @param L Lua状态机。
** @param 2 用于指定要移动元素的起始下标f。
** @param 3 用于指定要移动元素的结束下标e。
** @param 4 用于指定目标表的起始下标t。
** @param 5 可选参数，指定目标表。如果未提供，则默认为目标表1。
** @return 返回1，将目标表压入堆栈。
*/
static int tmove (lua_State *L) {
  lua_Integer f = luaL_checkinteger(L, 2); /* 起始下标 */
  lua_Integer e = luaL_checkinteger(L, 3); /* 结束下标 */
  lua_Integer t = luaL_checkinteger(L, 4); /* 目标下标 */
  int tt = !lua_isnoneornil(L, 5) ? 5 : 1;  /* 目标表的索引 */
  /* 检查参数表和目标表是否为表类型 */
  checktab(L, 1, TAB_R);
  checktab(L, tt, TAB_W);
  if (e >= f) {  /* 如果有元素需要移动 */
    lua_Integer n, i;
    /* 检查要移动的元素数量是否超出范围 */
    luaL_argcheck(L, f > 0 || e < LUA_MAXINTEGER + f, 3,
                  "too many elements to move要移动的元素过多");
    n = e - f + 1;  /* 需要移动的元素数量 */
    /* 检查目标下标是否会导致环绕 */
    luaL_argcheck(L, t <= LUA_MAXINTEGER - n + 1, 4,
                  "destination wrap around目标位置卷绕, 自动跳回到最小值");
    /* 根据起始和目标下标的相对位置选择合适的复制顺序 */
    if (t > e || t <= f || (tt != 1 && !lua_compare(L, 1, tt, LUA_OPEQ))) {
      /* 顺序复制 */
      for (i = 0; i < n; i++) {
        lua_geti(L, 1, f + i);
        lua_seti(L, tt, t + i);
      }
    }
    else {
      /* 逆序复制 */
      for (i = n - 1; i >= 0; i--) {
        lua_geti(L, 1, f + i);
        lua_seti(L, tt, t + i);
      }
    }
  }
  lua_pushvalue(L, tt);  /* 将目标表压入堆栈 */
  return 1;
}


/*
 * 将指定索引处的值添加到 luaL_Buffer 中。
 * L: Lua 状态机指针。
 * b: luaL_Buffer 指针，表示正在构建的字符串缓冲区。
 * i: 要添加的值在 Lua 表中的索引。
 */
static void addfield (lua_State *L, luaL_Buffer *b, lua_Integer i) {
  // 尝试通过索引 i 获取表中的元素
  lua_geti(L, 1, i);
  // 如果获取的元素不是字符串，则抛出错误
  if (l_unlikely(!lua_isstring(L, -1)))
    luaL_error(L, "invalid value 无效值(%s) at index 在表中用于 '连接'的索引 %I in table for 'concat'",
                  luaL_typename(L, -1), (LUAI_UACINT)i);
  // 将获取的元素添加到 luaL_Buffer b 中
  luaL_addvalue(b);
}


/*
 * 将Lua表中的字符串元素连接起来，并可选择性地在元素之间插入分隔符。
 * 
 * 参数:
 *   L - Lua状态机的指针。
 *   1 - 要连接的表。
 *   2 - 作为分隔符的可选字符串。如果未提供，则默认为空字符串。
 *   3 - 起始索引，默认为1。
 *   4 - 结束索引，默认为表中最后一个元素的索引。
 * 
 * 返回值:
 *   返回连接后的字符串。
 */

static int tconcat (lua_State *L) {
  luaL_Buffer b;  // 初始化一个luaL_Buffer结构，用于存储动态构建的字符串。
  lua_Integer last = aux_getn(L, 1, TAB_R); // 获取表中元素的数量。
  size_t lsep; // 分隔符字符串的长度。
  const char *sep = luaL_optlstring(L, 2, "", &lsep); // 获取分隔符字符串，如果未提供则默认为空字符串。
  lua_Integer i = luaL_optinteger(L, 3, 1); // 获取起始索引，默认为1。
  last = luaL_optinteger(L, 4, last); // 获取结束索引，默认为表中最后一个元素的索引。
  luaL_buffinit(L, &b); // 初始化luaL_Buffer。

  // 遍历表中指定范围的元素，将它们连接起来。
  for (; i < last; i++) {
    addfield(L, &b, i); // 将当前元素添加到构建的字符串中。
    luaL_addlstring(&b, sep, lsep); // 在当前元素后添加分隔符。
  }

  // 如果范围不为空，则将最后一个元素添加到结果中。
  if (i == last)  
    addfield(L, &b, i);
  
  luaL_pushresult(&b); // 将构建的字符串压入Lua栈。
  return 1; // 返回1，表示压入了一个结果。
}


/*
** {======================================================
** Pack/unpack 打包，解包
** =======================================================
*/

/*
 * tpack函数用于将Lua栈顶的多个元素打包成一个表，并将这个表放在Lua栈的顶部。
 * 参数：
 *   L：指向当前Lua状态机的指针。
 * 返回值：
 *   返回1，表示函数向Lua栈中压入了一个元素（即打包后的表）。
 */
static int tpack (lua_State *L) {
  int i;
  int n = lua_gettop(L);  /* 获取Lua栈顶元素的数量，即需要打包的元素个数 */
  lua_createtable(L, n, 1);  /* 创建一个拥有n个元素的表，并预留1个字段 */
  lua_insert(L, 1);  /* 将新创建的表移动到Lua栈的第1个位置 */
  for (i = n; i >= 1; i--)  /* 从n开始倒序将元素赋值给表的相应位置 */
    lua_seti(L, 1, i);
  lua_pushinteger(L, n);  /* 将元素个数n压入Lua栈 */
  lua_setfield(L, 1, "n");  /* 将元素个数n设置为表的字段"n" */
  return 1;  /* 表示函数执行完毕，并将表返回 */
}


/*
 * 将Lua栈中指定范围的元素解包为多个参数
 * 参数:
 *   L - Lua状态机指针
 * 返回值:
 *   返回解包元素的数量
 */
static int tunpack (lua_State *L) {
  lua_Unsigned n;
  // 从Lua栈中获取起始索引，默认为1
  lua_Integer i = luaL_optinteger(L, 2, 1);
  // 从Lua栈中获取结束索引，默认为栈顶元素的数量
  lua_Integer e = luaL_opt(L, luaL_checkinteger, 3, luaL_len(L, 1));
  // 如果起始索引大于结束索引，则范围为空，直接返回0
  if (i > e) return 0;  
  // 计算元素数量，减1以避免溢出
  n = (lua_Unsigned)e - i;  
  // 检查是否会有过多的结果导致解包失败
  if (l_unlikely(n >= (unsigned int)INT_MAX  ||
                 !lua_checkstack(L, (int)(++n))))
    return luaL_error(L, "too many results to unpack待解包的结果数量过多");
  // 将指定范围的元素压入Lua栈
  for (; i < e; i++) {  
    lua_geti(L, 1, i);
  }
  // 压入最后一个元素
  lua_geti(L, 1, e);  
  // 返回解包的元素数量
  return (int)n;
}

/* }====================================================== */



/*
** {======================================================
** Quicksort快速排序
** (based on 'Algorithms in MODULA-3', Robert Sedgewick;
**  Addison-Wesley, 1993.)
** =======================================================
*/


/* 
 * 定义数组索引的类型
 */
typedef unsigned int IdxT;

/*
 * 生成一个“随机”的无符号整数，用于随机化选择枢轴。这个宏仅在排序函数检测到分区结果有大的不平衡时使用。
 * （如果你不需要这种“随机性”，~0是一个好的选择。）
 */
#if !defined(l_randomizePivot)		/* { */

#include <time.h>

/* 定义sof宏，用于计算数组e的元素个数（以unsigned int为单位） */
#define sof(e)		(sizeof(e) / sizeof(unsigned int))

/*
** 以下代码块旨在通过time和clock函数获取"随机性"来源。由于我们不清楚'clock_t'和'time_t'的具体类型，
** 直接进行类型转换可能会有溢出的风险。因此，安全的方法是将它们的值复制到一个已知类型的数组中，
** 然后使用数组的值。
*/
/**
 * 生成一个基于当前时间和时钟的随机化 pivot 值。
 * 该函数利用当前的时钟和时间值作为基础，生成一个随机化的 pivot 值。
 * 这个 pivot 值可以用于各种算法中，如快速排序中选择基准元素。
 *
 * @return 返回一个基于当前时间和时钟的随机 unsigned int 值。
 */
static unsigned int l_randomizePivot (void) {
  // 获取当前的时钟读数和时间戳
  clock_t c = clock();
  time_t t = time(NULL);
  
  // 创建一个足够大的缓冲区来存储时钟和时间的二进制表示
  unsigned int buff[sof(c) + sof(t)];
  
  unsigned int i, rnd = 0; // rnd 用于存放最终的随机值
  
  // 将时钟和时间的二进制数据复制到缓冲区中
  memcpy(buff, &c, sof(c) * sizeof(unsigned int));
  memcpy(buff + sof(c), &t, sof(t) * sizeof(unsigned int));
  
  // 遍历缓冲区的所有字节，将其累加到 rnd 中，以增加随机性
  for (i = 0; i < sof(buff); i++)
    rnd += buff[i];
  
  return rnd; // 返回累加后的随机值
}

#endif					/* } */


/*
 * 定义了一个常量 RANLIMIT，该常量用于确定在进行某种数组操作（比如排序）时，
 * 是否采用随机化策略选择数组的基准元素（pivot）。当数组的大小超过 RANLIMIT 时，
 * 可能会采用随机化的基准元素选择策略，以避免某些特定的最坏情况发生。
 * 
 * RANLIMIT 的值被设置为 100u，即如果数组大小超过 100，就有可能使用随机化策略。
 */
#define RANLIMIT	100u


/**
 * 将指定的索引i和j的值设置到Lua栈上第一个元素所对应的表中。
 * 
 * @param L lua_State的指针，表示当前的Lua环境。
 * @param i 要设置的第一个索引。
 * @param j 要设置的第二个索引。
 * 
 * 该函数首先将Lua栈上第一个元素指定为表，然后分别使用索引i和j将值设置到该表中。
 * 注意该函数不返回任何值。
 */
static void set2 (lua_State *L, IdxT i, IdxT j) {
  lua_seti(L, 1, i); // 将索引i的值设置到Lua栈上第一个元素（表）中
  lua_seti(L, 1, j); // 将索引j的值设置到Lua栈上第一个元素（表）中
}


/*
** 此函数用于比较栈中两个索引位置的值的大小。
** 如果给定了排序的比较函数，它会使用该函数来比较；
** 如果没有给定比较函数，则默认使用 Lua 的小于操作符进行比较。
**
** @param L lua_State的指针，表示当前的Lua环境。
** @param a 第一个要比较的值的栈索引。
** @param b 第二个要比较的值的栈索引。
** @return 如果a小于b，返回1；如果a等于b，返回0；如果a大于b，返回-1。
*/
static int sort_comp (lua_State *L, int a, int b) {
  if (lua_isnil(L, 2))  /* 检查是否有提供的比较函数 */
    return lua_compare(L, a, b, LUA_OPLT);  /* 无比较函数时，使用Lua的默认比较 */
  else {  /* 有比较函数时 */
    int res;
    lua_pushvalue(L, 2);    /* 将比较函数压入栈中 */
    lua_pushvalue(L, a-1);  /* 压入第一个要比较的值，补偿函数占据的栈位置 */
    lua_pushvalue(L, b-2);  /* 压入第二个要比较的值，补偿函数和第一个值占据的栈位置 */
    lua_call(L, 2, 1);      /* 调用比较函数，2个输入参数，1个输出结果 */
    res = lua_toboolean(L, -1);  /* 获取比较函数的结果 */
    lua_pop(L, 1);          /* 弹出结果 */
    return res;
  }
}


/*
** 此函数用于执行分区操作：枢轴点 P 位于栈顶。
** 预条件：a[lo] <= P == a[up-1] <= a[up]，
** 因此只需要从 lo + 1 到 up - 2 进行分区操作。
** 后置条件：a[lo .. i - 1] <= a[i] == P <= a[i + 1 .. up]
** 返回值 'i'。
*/
static IdxT partition (lua_State *L, IdxT lo, IdxT up) {
  IdxT i = lo;  /* 将在首次使用前递增 */
  IdxT j = up - 1;  /* 将在首次使用前递减 */
  /* 循环不变量：a[lo .. i] <= P <= a[j .. up] */
  for (;;) {
    /* 下一个循环：重复 ++i 当 a[i] < P */
    while ((void)lua_geti(L, 1, ++i), sort_comp(L, -1, -2)) {
      if (l_unlikely(i == up - 1))  /* a[i] < P 但 a[up - 1] == P ？？ */
        luaL_error(L, "invalid order function for sorting用于排序的比较函数无效");
      lua_pop(L, 1);  /* 移除 a[i] */
    }
    /* 循环后，a[i] >= P 且 a[lo .. i - 1] < P */
    /* 下一个循环：重复 --j 当 P < a[j] */
    while ((void)lua_geti(L, 1, --j), sort_comp(L, -3, -1)) {
      if (l_unlikely(j < i))  /* j < i 但 a[j] > P ？？ */
        luaL_error(L, "invalid order function for sorting用于排序的比较函数无效");
      lua_pop(L, 1);  /* 移除 a[j] */
    }
    /* 循环后，a[j] <= P 且 a[j + 1 .. up] >= P */
    if (j < i) {  /* 没有错位的元素？ */
      /* a[lo .. i - 1] <= P <= a[j + 1 .. i .. up] */
      lua_pop(L, 1);  /* 移除 a[j] */
      /* 为了满足后置条件，交换枢轴点（a[up - 1]）和 a[i] */
      set2(L, up - 1, i);
      return i;
    }
    /* 否则，交换 a[i] 和 a[j] 以恢复不变量并重复 */
    set2(L, i, j);
  }
}


/*
** 选择[lo, up]中间区域（第2-3四分位数）的一个元素，通过'rnd'进行“随机化”选择。
**
** @param lo 范围的起始索引
** @param up 范围的结束索引
** @param rnd 用于“随机化”选择的无符号整数
** @return 返回选择的“随机”元素的索引
*/
static IdxT choosePivot (IdxT lo, IdxT up, unsigned int rnd) {
  IdxT r4 = (up - lo) / 4;  /* 计算范围的四分之一长度 */
  IdxT p = rnd % (r4 * 2) + (lo + r4); /* 使用'rnd'确定选择的元素位置 */
  lua_assert(lo + r4 <= p && p <= up - r4); /* 断言选择的元素确实在指定范围内 */
  return p;
}


/*
** 快速排序算法（递归函数）
**
** 参数:
** L - Lua状态机，用于操作Lua栈中的数据。
** lo - 排序区间的起始索引。
** up - 排序区间的结束索引。
** rnd - 用于选择枢轴元素的随机数，以避免最坏情况下的排序退化。
**
** 说明:
** 此函数通过递归地将待排序数组分为较小的子数组，并对子数组进行排序，实现了快速排序算法。
** 使用尾递归优化以减少函数调用开销。
*/
static void auxsort (lua_State *L, IdxT lo, IdxT up,
                                   unsigned int rnd) {
  while (lo < up) {  /* 通过尾递归循环来处理子数组 */
    IdxT p;  /* 枢轴索引 */
    IdxT n;  /* 后续使用 */
    /* 对索引'lo'、'p'和'up'指向的元素进行排序 */
    lua_geti(L, 1, lo);
    lua_geti(L, 1, up);
    if (sort_comp(L, -1, -2))  /* 如果a[up]小于a[lo]？ */
      set2(L, lo, up);  /* 交换a[lo]和a[up] */
    else
      lua_pop(L, 2);  /* 移除两个值 */
    if (up - lo == 1)  /* 如果只有两个元素？ */
      return;  /* 已经排序好了 */
    if (up - lo < RANLIMIT || rnd == 0)  /* 如果区间较小或不需要随机化？ */
      p = (lo + up)/2;  /* 中间元素是一个好的枢轴 */
    else  /* 对于较大的区间，值得选择一个随机的枢轴 */
      p = choosePivot(lo, up, rnd);
    lua_geti(L, 1, p);
    lua_geti(L, 1, lo);
    if (sort_comp(L, -2, -1))  /* 如果a[p]小于a[lo]？ */
      set2(L, p, lo);  /* 交换a[p]和a[lo] */
    else {
      lua_pop(L, 1);  /* 移除a[lo] */
      lua_geti(L, 1, up);
      if (sort_comp(L, -1, -2))  /* 如果a[up]小于a[p]？ */
        set2(L, p, up);  /* 交换a[up]和a[p] */
      else
        lua_pop(L, 2);
    }
    if (up - lo == 2)  /* 如果只有三个元素？ */
      return;  /* 已经排序好了 */
    lua_geti(L, 1, p);  /* 获取中间元素（枢轴） */
    lua_pushvalue(L, -1);  /* 推入枢轴 */
    lua_geti(L, 1, up - 1);  /* 推入a[up - 1] */
    set2(L, p, up - 1);  /* 交换枢轴（a[p]）和a[up - 1] */
    p = partition(L, lo, up);
    /* 确保 a[lo .. p - 1] <= a[p] == P <= a[p + 1 .. up] */
    if (p - lo < up - p) {  /* 如果较低区间的大小较小？ */
      auxsort(L, lo, p - 1, rnd);  /* 对较低区间递归调用 */
      n = p - lo;  /* 较小区间的大小 */
      lo = p + 1;  /* 尾递归调用 auxsort 用于 [p + 1 .. up] （较高区间） */
    }
    else {
      auxsort(L, p + 1, up, rnd);  /* 对较高区间递归调用 */
      n = up - p;  /* 较小区间的大小 */
      up = p - 1;  /* 尾递归调用 auxsort 用于 [lo .. p - 1] （较低区间） */
    }
    if ((up - lo) / 128 > n) /* 如果分区过于不平衡？ */
      rnd = l_randomizePivot();  /* 尝试新的随机化以选择枢轴 */
  }  /* 尾递归调用 auxsort(L, lo, up, rnd) */
}


/**
 * 对Lua栈中指定表进行排序。
 * 
 * @param L Lua状态机指针，指向需要排序的表所在的Lua环境。
 * @return 总是返回0，表示函数执行完毕，没有返回值。
 * 
 * 函数首先确定表中元素的数量，如果元素数量大于1，则进行排序。
 * 如果提供了第二个参数，则该参数必须是一个函数，用于比较两个元素的顺序。
 * 如果没有提供第二个参数，则使用默认的排序算法。
 */
static int sort (lua_State *L) {
  // 获取表中元素的数量
  lua_Integer n = aux_getn(L, 1, TAB_RW);
  if (n > 1) {  /* 非空且非单元素的表区间？ */
    // 检查表的元素数量是否超过INT_MAX，防止数组过大
    luaL_argcheck(L, n < INT_MAX, 1, "array too big数组太大");
    if (!lua_isnoneornil(L, 2))  /* 检查是否存在第二个参数 */
      // 如果存在第二个参数，必须是一个函数
      luaL_checktype(L, 2, LUA_TFUNCTION);  
    lua_settop(L, 2);  /* 确保栈顶是第二个参数，即排序函数 */
    // 执行排序
    auxsort(L, 1, (IdxT)n, 0);
  }
  return 0;
}

/* }====================================================== */


/*
 * 定义用于注册到Lua的表函数的数组。
 * 每个元素都是一个结构体，包含函数名和对应的C函数指针。
 * 功能包括：连接字符串、插入元素、打包、解包、移除元素、移动元素、排序。
 */
static const luaL_Reg tab_funcs[] = {
  {"concat", tconcat}, // 字符串连接
  {"连接", tconcat}, // 中文名称的字符串连接
  {"insert", tinsert}, // 在表中插入元素
  {"插入", tinsert}, // 中文名称的插入函数
  {"pack", tpack}, // 打包表为一个数组
  {"打包", tpack}, // 中文名称的打包函数
  {"unpack", tunpack}, // 解包数组为表
  {"解包", tunpack}, // 中文名称的解包函数
  {"remove", tremove}, // 从表中移除元素
  {"移除", tremove}, // 中文名称的移除函数
  {"move", tmove}, // 移动表中的元素
  {"移动", tmove}, // 中文名称的移动函数
  {"sort", sort}, // 对表进行排序
  {"排序", sort}, // 中文名称的排序函数  
  {NULL, NULL} // 结束标志
};


/**
 * 在Lua中打开一个名为"table"的库。
 * 
 * @param L 指向当前Lua状态机的指针，用于在Lua中创建和操作库。
 * @return 总是返回1，表示在Lua中创建了一个新的表。
 */
LUAMOD_API int luaopen_table (lua_State *L) {
  // 创建一个包含"tab_funcs"中定义的所有函数的新Lua库
  luaL_newlib(L, tab_funcs);
  return 1;
}

